package com.triple.triplerpc.loadbalancer;

import com.triple.triplerpc.model.ServiceMetaInfo;

import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * 一致性哈希负载均衡器
 */
public class ConsistentHashLoadBalancer implements LoadBalancer {

    /**
     * 一致性 Hash 环，存放虚拟节点
     * 使用 TreeMap 实现一致性 Hash 环，有 ceilingEntry 和 firstEntry 两个方法便于获取符合算法要求的节点。
     */
    private final TreeMap<Integer, ServiceMetaInfo> virtualNodes = new TreeMap<>();

    /**
     * 每个服务节点的虚拟节点数
     */
    private static final int VIRTUAL_NODE_NUM = 100;

    @Override
    public ServiceMetaInfo select(Map<String, Object> requestParams, List<ServiceMetaInfo> serviceMetaInfoList) {
        System.out.println(" >>> 当前采用的负载均衡策略 - 一致性哈希");
        if (serviceMetaInfoList.isEmpty()) {
            return null;
        }
        // 构建虚拟节点环: 将所有哈希值和服务信息成对存入TreeMap
        // 每次调用负载均衡器时, 都会重新构造 Hash 环, 为了能够即时处理服务节点的变化
        for (ServiceMetaInfo serviceMetaInfo : serviceMetaInfoList) {
            for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {
                int hash = getHash(serviceMetaInfo.getServiceAddress() + "#" + i);
                virtualNodes.put(hash, serviceMetaInfo);
            }
        }
        // 获取当前请求的哈希值
        int hash = getHash(requestParams);
        // 向上取整寻找大于等于当前请求哈希值的服务
        Map.Entry<Integer, ServiceMetaInfo> entry = virtualNodes.ceilingEntry(hash);
        // 如果不存在大于等于当前请求哈希值的服务, 则返回环首部的节点
        if (entry == null) {
            entry = virtualNodes.firstEntry();
        }
        return entry.getValue();
    }

    /**
     * Hash 算法
     *
     * @param key
     * @return
     */
    private int getHash(Object key) {
        return key.hashCode();
    }

}
